package com.yuanrui.practice.server.service.impl;

import com.alibaba.fastjson.JSON;
import com.yuanrui.practice.api.common.PageInfo;
import com.yuanrui.practice.api.common.PageResult;
import com.yuanrui.practice.api.req.GetPracticeSubjectsReq;
import com.yuanrui.practice.api.req.GetPreSetReq;
import com.yuanrui.practice.api.req.SubmitPracticeDetailReq;
import com.yuanrui.practice.api.vo.*;
import com.yuanrui.practice.server.dao.*;
import com.yuanrui.practice.server.entity.dto.CategoryDTO;
import com.yuanrui.practice.server.entity.dto.PracticeSetDTO;
import com.yuanrui.practice.server.entity.dto.PracticeSubjectDTO;
import com.yuanrui.practice.server.entity.po.*;
import com.yuanrui.practice.server.enums.CompleteStatusEnum;
import com.yuanrui.practice.server.enums.IsDeletedFlagEnum;
import com.yuanrui.practice.server.enums.SubjectInfoTypeEnum;
import com.yuanrui.practice.server.service.PracticeSetService;
import com.yuanrui.practice.server.subject.SubjectTypeHandlerFactory;
import com.yuanrui.practice.server.util.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

@Service
@Slf4j
public class PracticeSetServiceImpl implements PracticeSetService {

    @Resource
    private SubjectCategoryDao subjectCategoryDao;

    @Resource
    private SubjectMappingDao subjectMappingDao;

    @Resource
    private SubjectLabelDao subjectLabelDao;

    @Resource
    private SubjectInfoDao subjectDao;

    @Resource
    private PracticeSetDao practiceSetDao;

    @Resource
    private PracticeSetDetailDao practiceSetDetailDao;

    @Resource
    private SubjectTypeHandlerFactory subjectTypeHandlerFactory;

    @Resource
    private PracticeDetailDao practiceDetailDao;

    @Resource
    private PracticeDao practiceDao;

    /**
     * 获取专项练习内容
     *
     * @return 专项练习内容
     */
    @Override
    public List<SpecialPracticeVO> getSpecialPracticeContent() {
        List<SpecialPracticeVO> specialPracticeVOList = new LinkedList<>();
        //获取专项练习分类
        List<Integer> subjectTypeList = new LinkedList<>();
        subjectTypeList.add(SubjectInfoTypeEnum.RADIO.getCode());
        subjectTypeList.add(SubjectInfoTypeEnum.MULTIPLE.getCode());
        subjectTypeList.add(SubjectInfoTypeEnum.JUDGE.getCode());
        subjectTypeList.add(SubjectInfoTypeEnum.BRIEF.getCode());
        //获取大类
        CategoryDTO categoryDTO = new CategoryDTO();
        categoryDTO.setSubjectTypeList(subjectTypeList);
        List<PrimaryCategoryPO> poList = subjectCategoryDao.getPrimaryCategory(categoryDTO);
        if (CollectionUtils.isEmpty(poList)) {
            return Collections.emptyList();
        }
        poList.forEach(po -> {
            SpecialPracticeVO specialPracticeVO = new SpecialPracticeVO();
            specialPracticeVO.setPrimaryCategoryId(po.getParentId());
            CategoryPO categoryPO = subjectCategoryDao.selectById(po.getParentId());
            specialPracticeVO.setPrimaryCategoryName(categoryPO.getCategoryName());
            //查大类下的小类
            CategoryDTO categorySecond = new CategoryDTO();
            categorySecond.setCategoryType(2);
            categorySecond.setParentId(po.getParentId());
            List<CategoryPO> smallPolist = subjectCategoryDao.selectList(categorySecond);
            if (CollectionUtils.isEmpty(smallPolist)) {
                return;
            }
            List<SpecialPracticeCategoryVO> categoryVOList = new LinkedList<>();
            smallPolist.forEach(smallPo -> {
                //查标分类下标签
                List<SpecialPracticeLabelVO> labeVOList = getLabelVOList(smallPo.getId(), subjectTypeList);
                if (CollectionUtils.isEmpty(labeVOList)) {
                    return;
                }
                SpecialPracticeCategoryVO specialPracticeCategoryVO = new SpecialPracticeCategoryVO();
                specialPracticeCategoryVO.setCategoryId(smallPo.getId());
                specialPracticeCategoryVO.setCategoryName(smallPo.getCategoryName());
                List<SpecialPracticeLabelVO> labelList = new LinkedList<>();
                labeVOList.forEach(label -> {
                    SpecialPracticeLabelVO specialPracticeLabelVO = new SpecialPracticeLabelVO();
                    specialPracticeLabelVO.setLabelName(label.getLabelName());
                    specialPracticeLabelVO.setId(label.getId());
                    specialPracticeLabelVO.setAssembleId(label.getAssembleId());
                    labelList.add(specialPracticeLabelVO);
                });
                specialPracticeCategoryVO.setLabelList(labelList);
                categoryVOList.add(specialPracticeCategoryVO);
            });
            specialPracticeVO.setCategoryList(categoryVOList);
            specialPracticeVOList.add(specialPracticeVO);
        });
        return specialPracticeVOList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PracticeSetVO addPractice(PracticeSubjectDTO dto) {

        PracticeSetVO setVO = new PracticeSetVO();
        List<PracticeSubjectDetailVO> practiceList = getPracticeList(dto);
        if (CollectionUtils.isEmpty(practiceList)) {
            return setVO;
        }
        PracticeSetPO practiceSetPO = new PracticeSetPO();
        practiceSetPO.setSetType(1);
        List<String> assembleIds = dto.getAssembleIds();
        Set<Long> categoryIdSet = new HashSet<>();
        assembleIds.forEach(assembleId -> {
            Long categoryId = Long.valueOf(assembleId.split("-")[0]);
            categoryIdSet.add(categoryId);
        });
        StringBuffer setName = new StringBuffer();
        int i = 1;
        for (Long categoryId : categoryIdSet) {
            if (i > 2) {
                break;
            }
            CategoryPO categoryPO = subjectCategoryDao.selectById(categoryId);
            setName.append(categoryPO.getCategoryName());
            setName.append("、");
            i = i + 1;
        }
        setName.deleteCharAt(setName.length() - 1);
        if (i == 2) {
            setName.append("专项练习");
        } else {
            setName.append("等专项练习");
        }
        practiceSetPO.setSetName(setName.toString());
        String labelId = assembleIds.get(0).split("-")[1];
        SubjectLabelPO labelPO = subjectLabelDao.selectById(Long.valueOf(labelId));
        practiceSetPO.setPrimaryCategoryId(labelPO.getCategoryId());
        practiceSetPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        practiceSetPO.setCreatedBy(LoginUtil.getLoginId());
        practiceSetPO.setCreatedTime(new Date());
        practiceSetDao.add(practiceSetPO);
        Long practiceSetId = practiceSetPO.getId();

        //思考，这里哪里不符合规范，配合听视频的延伸
        List<PracticeSetDetailPO> detailPOList = new LinkedList<>();
        practiceList.forEach(e -> {
            PracticeSetDetailPO detailPO = new PracticeSetDetailPO();
            detailPO.setSetId(practiceSetId);
            detailPO.setSubjectId(e.getSubjectId());
            detailPO.setSubjectType(e.getSubjectType());
            detailPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
            detailPO.setCreatedBy(LoginUtil.getLoginId());
            detailPO.setCreatedTime(new Date());
            detailPOList.add(detailPO);
        });
        practiceSetDetailDao.insertBatch(detailPOList);
        setVO.setSetId(practiceSetId);
        return setVO;
    }

    @Override
    public PracticeSubjectListVO getSubjects(GetPracticeSubjectsReq req) {
        PracticeSubjectListVO practiceSubjectListVO = new PracticeSubjectListVO();
        PracticeSetPO practiceSetPO = practiceSetDao.queryById(req.getSetId());
        practiceSubjectListVO.setTitle(practiceSetPO.getSetName());
        List<PracticeSubjectDetailVO> subjectList = new LinkedList<>();
        List<PracticeSetDetailPO> practiceSetDetailPOS = practiceSetDetailDao.queryBySetId(req.getSetId());
        if (CollectionUtils.isEmpty(practiceSetDetailPOS)) {
            return practiceSubjectListVO;
        }
        String loginId = LoginUtil.getLoginId();
        Long practice = req.getPracticeId();
        practiceSetDetailPOS.forEach(subject -> {
            PracticeSubjectDetailVO practiceSubjectDetailVO = new PracticeSubjectDetailVO();
            practiceSubjectDetailVO.setSubjectId(subject.getSubjectId());
            practiceSubjectDetailVO.setSubjectType(subject.getSubjectType());
            if (Objects.nonNull(practice)) {
                PracticeDetailPO practiceDetailPO = practiceDetailDao.selectDetail(practice, subject.getSubjectId(), loginId);
                if (Objects.nonNull(practiceDetailPO) && StringUtils.isNotBlank(practiceDetailPO.getAnswerContent())) {
                    practiceSubjectDetailVO.setIsAnswer(1);
                } else {
                    practiceSubjectDetailVO.setIsAnswer(0);
                }
            }
            subjectList.add(practiceSubjectDetailVO);
        });
        practiceSubjectListVO.setSubjectList(subjectList);

        if (Objects.isNull(practice)) {
            //第一次练习
            Long newPracticeId = insertUnCompletePratice(req.getSetId(), loginId);
            practiceSubjectListVO.setPracticeId(newPracticeId);
        } else {
            //更新未完成练习
            updateUnCompletePratice(practice);
            PracticePO practicePO = practiceDao.selectById(practice);
            practiceSubjectListVO.setPracticeId(practice);
            practiceSubjectListVO.setTimeUse(practicePO.getTimeUse());
        }
        return practiceSubjectListVO;
    }

    /**
     * 更新未完成练习
     *
     * @param practice 练习id
     */
    private void updateUnCompletePratice(Long practice) {
        PracticePO practicePO = new PracticePO();
        practicePO.setId(practice);
        practicePO.setSubmitTime(new Date());
        practiceDao.update(practicePO);
    }

    private Long insertUnCompletePratice(Long id, String loginId) {
        PracticePO practicePO = new PracticePO();
        practicePO.setSetId(id);
        practicePO.setCompleteStatus(CompleteStatusEnum.UNFINISHED.getCode());
        practicePO.setCreatedBy(loginId);
        practicePO.setCreatedTime(new Date());
        practicePO.setCorrectRate(new BigDecimal("0.00"));
        practicePO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        practicePO.setSubmitTime(new Date());
        practicePO.setTimeUse("00:00:00");
        practiceDao.insert(practicePO);
        return practicePO.getId();
    }

    @Override
    public PracticeSubjectVO getPracticeSubject(PracticeSubjectDTO practiceSubjectDTO) {
        PracticeSubjectVO practiceSubjectVO = new PracticeSubjectVO();
        SubjectPO subjectPO = subjectDao.selectById(practiceSubjectDTO.getSubjectId());
        practiceSubjectVO.setSubjectName(subjectPO.getSubjectName());
        practiceSubjectVO.setSubjectType(subjectPO.getSubjectType());
        List<PracticeSubjectOptionVO> query = subjectTypeHandlerFactory.getHandler(subjectPO.getSubjectType()).query(practiceSubjectDTO.getSubjectId());
        practiceSubjectVO.setOptionList(query);
        return practiceSubjectVO;
    }

    @Override
    public PageResult<PracticeSetVO> getPreSetContent(PracticeSetDTO dto) {
        PageResult<PracticeSetVO> pageResult = new PageResult<>();
        PageInfo pageInfo = dto.getPageInfo();
        pageResult.setPageNo(pageInfo.getPageNo());
        pageResult.setPageSize(pageInfo.getPageSize());
        int start = (pageInfo.getPageNo() - 1) * pageInfo.getPageSize();
        Integer count = practiceSetDao.getListCount(dto);
        if (count == 0) {
            return pageResult;
        }
        List<PracticeSetPO> setPOList = practiceSetDao.getSetList(dto, start, dto.getPageInfo().getPageSize());
        if (log.isInfoEnabled()) {
            log.info("获取的模拟考卷列表{}", JSON.toJSONString(setPOList));
        }
        List<PracticeSetVO> list = new LinkedList<>();
        setPOList.forEach(e -> {
            PracticeSetVO vo = new PracticeSetVO();
            vo.setSetId(e.getId());
            vo.setSetName(e.getSetName());
            vo.setSetHeat(e.getSetHeat());
            vo.setSetDesc(e.getSetDesc());
            list.add(vo);
        });
        pageResult.setResult(list);
        pageResult.setTotal(count);
        return pageResult;
    }

    /**
     * 获取练习题目列表
     *
     * @param dto 入参
     * @return 练习题目列表
     */
    private List<PracticeSubjectDetailVO> getPracticeList(PracticeSubjectDTO dto) {
        List<PracticeSubjectDetailVO> practiceList = new LinkedList<>();

        //避免重复题目
        List<Long> excludeSubjectIds = new LinkedList<>();
        //设置题目数量,后面优化到nacos
        Integer radioSubjectCount = 10;
        Integer multipleSubjectCount = 6;
        Integer judgeSubjectCount = 4;
        Integer totalSubjectCount = 20;

        //获取单选题
        dto.setSubjectCount(radioSubjectCount);
        dto.setSubjectType(SubjectInfoTypeEnum.RADIO.getCode());
        assembleList(dto, practiceList, excludeSubjectIds);

        //获取多选题
        dto.setSubjectCount(multipleSubjectCount);
        dto.setSubjectType(SubjectInfoTypeEnum.MULTIPLE.getCode());
        assembleList(dto, practiceList, excludeSubjectIds);

        //获取判断题
        dto.setSubjectCount(judgeSubjectCount);
        dto.setSubjectType(SubjectInfoTypeEnum.JUDGE.getCode());
        assembleList(dto, practiceList, excludeSubjectIds);

        //补充题目
        if (practiceList.size() == totalSubjectCount) {
            return practiceList;
        }
        //剩余题目数量,用单选题补充
        Integer remainCount = totalSubjectCount - practiceList.size();
        dto.setSubjectCount(remainCount);
        dto.setSubjectType(1);
        assembleList(dto, practiceList, excludeSubjectIds);
        return practiceList;
    }

    /**
     * 组装题目列表
     *
     * @param dto               入参
     * @param practiceList      题目列表
     * @param excludeSubjectIds 要排除的题目id
     */
    private List<PracticeSubjectDetailVO> assembleList(PracticeSubjectDTO dto, List<PracticeSubjectDetailVO> practiceList, List<Long> excludeSubjectIds) {
        dto.setExcludeSubjectIds(excludeSubjectIds);
        List<SubjectPO> subjectPOList = subjectDao.getPracticeSubject(dto);
        if (CollectionUtils.isEmpty(subjectPOList)) {
            return practiceList;
        }
        subjectPOList.forEach(subjectPO -> {
            PracticeSubjectDetailVO practiceSubjectDetailVO = new PracticeSubjectDetailVO();
            practiceSubjectDetailVO.setSubjectId(subjectPO.getId());
            practiceSubjectDetailVO.setSubjectType(subjectPO.getSubjectType());
            practiceList.add(practiceSubjectDetailVO);
            excludeSubjectIds.add(subjectPO.getId());
        });
        return practiceList;
    }

    private List<SpecialPracticeLabelVO> getLabelVOList(Long categoryId, List<Integer> subjectTypeList) {
        List<LabelCountPO> labelCountPOS = subjectMappingDao.getLabelList(categoryId, subjectTypeList);
        if (CollectionUtils.isEmpty(labelCountPOS)) {
            return Collections.emptyList();
        }
        List<SpecialPracticeLabelVO> labelVOList = new LinkedList<>();
        labelCountPOS.forEach(labelCountPO -> {
            SpecialPracticeLabelVO specialPracticeLabelVO = new SpecialPracticeLabelVO();
            specialPracticeLabelVO.setId(labelCountPO.getLabelId());
            specialPracticeLabelVO.setAssembleId(categoryId + "-" + labelCountPO.getLabelId());
            SubjectLabelPO subjectLabelPO = subjectLabelDao.selectById(labelCountPO.getLabelId());
            specialPracticeLabelVO.setLabelName(subjectLabelPO.getLabelName());
            labelVOList.add(specialPracticeLabelVO);
        });
        return labelVOList;
    }
}
